package org.ww.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.ww.cart.feign.ProductFeignClient;
import org.ww.cart.interceptor.CartInterceptor;
import org.ww.cart.service.CartService;
import org.ww.cart.to.UserInfoTo;
import org.ww.cart.vo.CartItemVo;
import org.ww.cart.vo.CartVo;
import org.ww.common.to.SkuInfoTo;
import org.ww.common.utils.R;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @program: gmall
 * @description: 购物车实现类
 * @author: ww
 * @create: 2024-05-17 13:22
 **/
@Service
public class CartServiceImpl implements CartService {

    private static final String CARTKEY = "gmall:cart:";
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public CartItemVo addCartItem(Long skuId, Integer num) throws ExecutionException, InterruptedException {

        //获取当前操作的用户购物车 key:用户id  object:skuid  object:skuinfo
        BoundHashOperations<String,Object,Object> boundHashOperations = getUserCurrentCart();

        //当前购物车无此商品
        String cartString = (String)boundHashOperations.get(skuId.toString());
        if(StringUtils.isBlank(cartString)){
            CartItemVo vo = new CartItemVo();
            CompletableFuture<Void> getSkuInfoFuture = CompletableFuture.runAsync(() -> {
                //1、远程调用商品服务，根据skuId获取商品信息
                R skuInfoR = productFeignClient.getSkuInfo(skuId);
                SkuInfoTo skuInfo = skuInfoR.getData("skuInfo", new TypeReference<SkuInfoTo>() {
                });

                //2、商品添加到购物车
                vo.setCheck(Boolean.TRUE);
                vo.setCount(num);
                vo.setImage(skuInfo.getSkuDefaultImg());
                vo.setTitle(skuInfo.getSkuTitle());
                vo.setSkuId(skuId);
                vo.setPrice(skuInfo.getPrice());
            }, threadPoolTaskExecutor);

            //3、远程查询sku的组合信息
            CompletableFuture<Void> getSkuSaleAttrValuesFuture = CompletableFuture.runAsync(() -> {
                List<String> skuSaleAttrValues = productFeignClient.getSkuSaleAttrValues(skuId);
                vo.setSkuAttrValues(skuSaleAttrValues);
            }, threadPoolTaskExecutor);

            //等上边两个任务都执行完成才能向redis中放数据
            CompletableFuture.allOf(getSkuInfoFuture, getSkuSaleAttrValuesFuture).get();
            String resultJson = JSON.toJSONString(vo);
            boundHashOperations.put(skuId.toString(),resultJson);
            return vo;
        }else{
            //如果存在该skuid的商品，则将商品数量累加
            CartItemVo cartItemVo = JSON.parseObject(cartString, CartItemVo.class);
            cartItemVo.setCount(cartItemVo.getCount() + num);
            String resultJson = JSON.toJSONString(cartItemVo);
            boundHashOperations.put(skuId.toString(),resultJson);
            return cartItemVo;
        }
    }

    @Override
    public CartItemVo getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> userCurrentCart = getUserCurrentCart();
        String item = (String)userCurrentCart.get(skuId.toString());
        CartItemVo cartItemVo = JSON.parseObject(item, CartItemVo.class);
        return cartItemVo;
    }

    /**
     * 获取购物车信息
     * 使用拦截器，将用户信息封装起来
     * 用一个user-key判断当前用户是否是临时用户
     * 如果存在userid，则登录了，用登录的信息
     * 如果不存在则用user-key，使用临时用户
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Override
    public CartVo getCart() throws ExecutionException, InterruptedException {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        Long userId = userInfoTo.getUserId();
        String userKey = userInfoTo.getUserKey();
        CartVo cartVo = new CartVo();
        //判断用户是否登录
        if(null != userId){
            //1、用户已经登录了，将临时购物车和用户购物车进行合并
            String cartUserId = CARTKEY + userId;
            String cartUserKey = CARTKEY + userKey;
            //将临时购物车进行合并
            List<CartItemVo> cartItems = getCartItemsByKey(cartUserKey);
            if(CollectionUtils.isNotEmpty(cartItems)){
                for (CartItemVo cartItem : cartItems) {
                    addCartItem(cartItem.getSkuId(),cartItem.getCount());
                }
                //清除临时购物车数据
                clearTempCartData(cartUserKey);
            }
            //获取合并后的购物车的数据
            List<CartItemVo> cartItemsVo = getCartItemsByKey(cartUserId);
            cartVo.setItems(cartItemsVo);
        }else{
            //2、用户没有登录，获取临时购物车数据
            String key = CARTKEY + userKey;
            List<CartItemVo> itemList = getCartItemsByKey(key);
            cartVo.setItems(itemList);
        }

        return cartVo;
    }

    @Override
    public void checkItem(Long skuId, Integer check) {
        BoundHashOperations<String, Object, Object> userCurrentCart = getUserCurrentCart();
        CartItemVo cartItem = getCartItem(skuId);
        cartItem.setCheck(check == 1 ? true:false);
        String jsonString = JSON.toJSONString(cartItem);
        userCurrentCart.put(skuId.toString(),jsonString);
    }

    @Override
    public void countItem(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> userCurrentCart = getUserCurrentCart();
        CartItemVo cartItem = getCartItem(skuId);
        cartItem.setCount(num);
        String jsonString = JSON.toJSONString(cartItem);
        userCurrentCart.put(skuId.toString(),jsonString);
    }

    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations<String, Object, Object> userCurrentCart = getUserCurrentCart();
        userCurrentCart.delete(skuId.toString());
    }

    @Override
    public List<CartItemVo> getUserCartItems() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        if(userInfoTo.getUserId() == null){
            return null;
        }
        String cartUserKey = CARTKEY + userInfoTo.getUserId();
        List<CartItemVo> cartItems = getCartItemsByKey(cartUserKey);
        List<Long> skuIds = Optional.ofNullable(cartItems)
                .orElseGet(ArrayList::new)
                .stream()
                .map(CartItemVo::getSkuId)
                .distinct()
                .collect(Collectors.toList());
        R r = productFeignClient.getPrice(skuIds);
        Map<Long, BigDecimal> skuPriceMap = r.getData(new TypeReference<Map<Long, BigDecimal>>() {
        });
        for (CartItemVo cartItem : cartItems) {
            cartItem.setPrice(skuPriceMap.get(cartItem.getSkuId()));
        }
        return cartItems;
    }

    private void clearTempCartData(String cartUserKey) {
        redisTemplate.delete(cartUserKey);
    }

    private List<CartItemVo> getCartItemsByKey(String key) {
        BoundHashOperations<String, Object, Object> boundHashOperations = redisTemplate.boundHashOps(key);
        List<Object> values = boundHashOperations.values();
        return Optional.ofNullable(values)
                .orElseGet(ArrayList::new)
                .stream()
                .map(v -> {
                    String jsonData = (String) v;
                    CartItemVo cartItemVo = JSON.parseObject(jsonData, CartItemVo.class);
                    return cartItemVo;
                }).collect(Collectors.toList());
    }

    private BoundHashOperations<String, Object, Object> getUserCurrentCart() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if(null != userInfoTo.getUserId()){
            cartKey = CARTKEY + userInfoTo.getUserId();
        }else{
            cartKey = CARTKEY + userInfoTo.getUserKey();
        }
        BoundHashOperations<String, Object, Object> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        return boundHashOperations;
    }
}
